package com.delayqueue.common.netty;


import com.delayqueue.common.util.NettyMessageUtil;
import com.delayqueue.common.util.RandomUtil;
import io.netty.channel.ChannelHandlerContext;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class NodeCache {

    private static Integer _index = 0;
    protected ConcurrentHashMap<String, Node> nodeMap = new ConcurrentHashMap<>();
    protected List<String> nodeNameList = Collections.synchronizedList(new LinkedList<>());

    public Node get(String ip, int port) {
        return nodeMap.get(ip + ":" + port);
    }


    public Node chooseNode() {
        if (nodeNameList.isEmpty()) {
            return null;
        }
        String nodeName;
        synchronized (_index) {
            if (_index >= nodeNameList.size()) {
                _index = 0;
            }
            nodeName = nodeNameList.get(_index);
            _index++;
        }
        return nodeMap.get(nodeName);
    }

    public Node chooseNodeRandom() {
        if (nodeNameList.isEmpty()) {
            return null;
        }
        int size = nodeNameList.size();
        int _index = RandomUtil.random(size);
        String nodeName = nodeNameList.get(_index);
        return nodeMap.get(nodeName);
    }

    public void broadCastMessage(NettyMessage message) {
        List<ChannelHandlerContext> contextList = new ArrayList<>();
        synchronized (nodeNameList) {
            for (String s : nodeNameList) {
                Node node = nodeMap.get(s);
                if (node != null) {
                    contextList.add(node.getChannelHandlerContext());
                }
            }
        }
        for (ChannelHandlerContext context : contextList) {
            NettyMessageUtil.send(context, message);
        }
    }

    public boolean delete(String ip, int port) {
        return delete(new Node(ip, port));
    }

    public List<String> getAllConnectAddress() {
        List<String> list = new ArrayList<>();
        synchronized (nodeNameList) {
            for (String s : nodeNameList) {
                Node node = nodeMap.get(s);
                if (node != null) {
                    list.add(node.getIp() + ":" + node.getConnectPort());
                }
            }
        }
        return list;
    }

    public void add(String ip, int port) {
        add(new Node(ip, port));
    }

    public void add(String ip, int port, ChannelHandlerContext ctx) {
        Node node = new Node(ip, port);
        node.setChannelHandlerContext(ctx);
        add(node);
    }

    public void add(Node node) {
        delete(node);
        nodeMap.put(node.name(), node);
        synchronized (nodeNameList) {
            nodeNameList.add(node.name());
        }
    }

    public boolean delete(Node node) {
        String nodeName = node.name();
        boolean isHave = nodeMap.containsKey(nodeName);
        if (isHave) {
            ChannelHandlerContext context = nodeMap.get(node.name()).getChannelHandlerContext();
            if (context != null) {
                context.channel().close();
                context.close();
                nodeMap.remove(nodeName);
            }
        }
        synchronized (nodeNameList) {
            nodeNameList.remove(nodeName);
        }
        return isHave;
    }

    public List<Node> getAllNode() {
        List<Node> nodeList;
        synchronized (nodeNameList) {
            nodeList = nodeNameList.stream().map(o -> nodeMap.get(o)).collect(Collectors.toList());
        }
        return nodeList;
    }
}
